﻿#region Using Statements
using System.Diagnostics;
using DirectZZT.Shared.Core;
using DirectZZT.Shared.Gameplay;
using DirectZZT.Shared.Input;
using DirectZZT.Shared.World;
using DirectZZT.Shared.World.Objects;
using Microsoft.Xna.Framework;
#endregion

namespace DirectZZT.BoardObjects
{
    /// <summary>
    /// Special board object that is actually the player.
    /// </summary>
    [Export("Player")]
    [BoardObject(Serialize = false, Identifier = "Player", AllowBecome = false, Classification = BoardObjectClassification.Item)]
    public sealed class PlayerBoardObject : BoardObject, ICreatureBoardObject, IPlayerBoardObject
    {
        #region Constants

        /// <summary>
        /// Defines the interval in milliseconds when the player's controls are updated.
        /// TODO: find out about true interval! 66 was chosen by fair dice roll...
        /// </summary>
        private const int PlayerControlsUpdateRate = 66;

        #endregion

        #region Fields

        private IInputManager _input;
        private IGameController _gameController;
        private Stopwatch _sw;
        private Direction _lastShotDirection = Direction.East;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the PlayerBoardObject class.
        /// </summary>
        public PlayerBoardObject()
        {
            this.Texture = "Textures/PlayfieldObjects/Player";
            this.Enabled = true;
            this.Visible = true;

            _sw = new Stopwatch();

            // create dummy data right here
            this.Player = new Shared.Gameplay.Player();
            this.Player.BoardObject = this;
            this.Player.PlayerBoardObject = this;
        }

        #endregion

        #region BoardObject Members

        /// <summary>
        /// Returns the capabilities.
        /// </summary>
        public override BoardObjectCapabilities Capabilities
        {
            get { return base.Capabilities | BoardObjectCapabilities.Movable; }
        }

        /// <summary>
        /// Not even in DirectZZT supported.
        /// </summary>
        /// <param name="featureSet"></param>
        /// <returns></returns>
        protected override bool CanAttachScript(FeatureSet featureSet)
        {
            return false;
        }

        /// <summary>
        /// Called when this board object has collided with another.
        /// </summary>
        /// <param name="args">The object that collided with us.</param>
        /// <returns>The resolve args, which determine what shall happen when the logic has concluded.</returns>
        public override CollisionResolveArgs OnCollide(CollisionArgs args)
        {
            CollisionResolveArgs result = base.OnCollide(args);

            // check what did hit us and either die or do something else
            // was it an item?
            if (args.Object is IItemBoardObject)
            {
                IItemBoardObject item = (IItemBoardObject)args.Object;
                // can we pick this item up?
                if (item.CanPickup(this))
                {
                    // pick the item up
                    item.OnPickup(this);
                    // signal that the item goes away
                    // TODO: shouldn't the item disappear by itself?
                    result.RemoveTarget = true;
                }
            }

            return result;
        }

        /// <summary>
        /// Called on initialization.
        /// </summary>
        public override void OnAttach()
        {
            base.OnAttach();

            _input = this.Board.World.GetService(typeof(IInputManager)) as IInputManager;
            _gameController = this.Board.World.GetService(typeof(IGameController)) as IGameController;

            _sw.Start();
        }

        /// <summary>
        /// Updates the player and checks the inputs to move.
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            // "emulate" ZZT - update controls every one in a while
            if (_sw.ElapsedMilliseconds >= PlayerControlsUpdateRate)
            {
                // don't update if the game is paused
                if (!_gameController.IsGamePaused)
                {
                    UpdateControls();
                }
                _sw.Restart();
            }
        }

        private void UpdateControls()
        {
            if (!_input.GetActionInvoked("Shoot"))
            {
                if (_input.GetActionInvoked("Up"))
                {
                    this.MoveNorth();
                }
                if (_input.GetActionInvoked("Down"))
                {
                    this.MoveSouth();
                }
                if (_input.GetActionInvoked("Left"))
                {
                    this.MoveWest();
                }
                if (_input.GetActionInvoked("Right"))
                {
                    this.MoveEast();
                }
            }
            else
            {
                TryShootBullet(false);
            }
            //if (_input.GetActionInvoked("ShootLast"))
            //{
            //    TryShootBullet(true);
            //}

        }

        /// <summary>
        /// Attempts to shoot a bullet. If no ammo is left, this method does nothing but displays a text.
        /// </summary>
        /// <param name="lastDirection">Whether or not to shoot into the last shot direction.</param>
        private void TryShootBullet(bool lastDirection)
        {
            // only when there are bullets/arrows left
            if (this.Player.Inventory.AmmoAmount > 0)
            {
                // prepare parameters
                Direction direction = Direction.None;

                // find out in which direction to shoot (also the offset). then alter the texture rotation according to the 
                if (lastDirection)
                {
                    direction = _lastShotDirection;
                }
                else
                {
                    if (_input.GetActionInvoked("Up")) { direction = Direction.North; }
                    else if (_input.GetActionInvoked("Down")) { direction = Direction.South; }
                    else if (_input.GetActionInvoked("Left")) { direction = Direction.West; }
                    else if (_input.GetActionInvoked("Right")) { direction = Direction.East; }
                    else { return; }
                }

                IProjectileBoardObject bullet = BulletBoardObject.FireBullet(this, direction);

                // spawn object. when shot too rapidly, no bullet is spawned
                if (bullet != null)
                {
                    // only if a bullet was spawned reduce the ammo!
                    this.Player.Inventory.AmmoAmount--;
                    // remember direction
                    _lastShotDirection = direction;
                }
            }
            else
            {
                // TODO: show message
            }
        }

        #endregion

        #region ICreatureBoardObject Members

        /// <summary>
        /// Gets/sets the health of this creature.
        /// </summary>
        public int Health
        {
            get { return Player.Health; }
            set { Player.Health = value; }
        }
        /// <summary>
        /// Gets/sets the armor of this creature.
        /// </summary>
        public int Armor
        {
            get { return Player.Armor; }
            set { Player.Armor = value; }
        }

        /// <summary>
        /// Inflicts the given amount of damage on this creature.
        /// The actual damage taken away from Health and Armor are being calculated by the creature.
        /// </summary>
        /// <param name="value">The amount of damage to inflict.</param>
        public void InflictDamage(int value)
        {
            //// when there is armor left...
            //if (Armor > 0)
            //{
            //    int armorInflicted = Math.Min(value / 2, this.Armor);
            //    this.Armor -= armorInflicted;
            //}

            if (Health - value > 0)
            {
                Health -= value;
            }
            else
            {
                // end the game (the player's dead!
            }
        }

        #endregion

        #region IPlayerBoardObject Members

        /// <summary>
        /// Gets the player instance that is attached to this board object.
        /// </summary>
        public Shared.Gameplay.Player Player { get; private set; }

        #endregion
    }
}
